#region Microsoft Public License
/*
 * (c) Copyright Chenjun Wu. http://utablesdk.codeplex.com
 * 
 * This source is subject to the Microsoft Public License (Ms-PL).
 * Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
 * All other rights reserved.
 */
#endregion

﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using UTable.Objects.Controls;
using UTable.Input;
using UTable.Input.MultiTouch;
using System.IO;
using UTable.ObjectManager.Commands;
using UTable.Objects.Policies;
using System.Windows.Threading;
using System.Collections.ObjectModel;
using System.Windows.Media.Animation;
using UTable.Objects.Shapes;


namespace UTable.Objects
{
    public class UTabletop: UObject
    {
        #region Private Fields

        Dictionary<int, DispatcherTimer> _holdFingersList = new Dictionary<int, DispatcherTimer>();
        Dictionary<int, Point> _holdFingerPositionList = new Dictionary<int, Point>();
        Dictionary<UElementMenu, int> _menuList = new Dictionary<UElementMenu, int>();
        Dictionary<UElementMenu, Point> _menuPositionList = new Dictionary<UElementMenu, Point>();
        int topZindex = 0;
        readonly int holdRadius = 10;    // pixels
        readonly int holdTime = 1000;   // Milliseconds

        #endregion

        #region Constructor

        public UTabletop()
        {
            PhysicalLayoutPolicyParameter parameter = new PhysicalLayoutPolicyParameter(true, 0.6d, 0.45d);
            parameter.CollisionPolicy = new TabletopCollisionPolicy();
			OnCreatePhysicalPolicy(parameter);
            this.LayoutPolicyParameter = parameter;
            this.LayoutPolicyType = typeof(PhysicalLayoutPolicy);
            this.InputReceived += new UTable.Input.InputEventHandler(UTabletop_InputReceived);
            this.MenuDisplay = UObjectMenuDisplayType.Hide;
            this.ShowsActivationEffects = false;
        }

		protected virtual void OnCreatePhysicalPolicy(PhysicalLayoutPolicyParameter parameter)
		{

		}

        #endregion

        #region Private Methods

        void UTabletop_InputReceived(UTable.Input.InputEventArgs args)
        {
            if (args is MultiTouchEventArgs && !args.Handled)
            {
                UTabletop_MultiTouchReceived(args as MultiTouchEventArgs);
            }
        }

        void UTabletop_MultiTouchReceived(MultiTouchEventArgs args)
        {
            foreach (FingerEventArgs f in args.FingerEvents)
            {
                switch (f.EventType)
                {
                    case FingerEventType.FINGER_DOWN_EVENT:
                        {
                            DispatcherTimer startTimer = new DispatcherTimer();
                            startTimer.Tick += new EventHandler(startTimer_Elapsed);
                            startTimer.Interval = TimeSpan.FromMilliseconds(holdTime);
                            _holdFingersList.Add(f.FingerID, startTimer);
                            _holdFingerPositionList.Add(f.FingerID, f.Position);

                            PiePiece pie = new PiePiece();
                            pie.Radius = 24;
                            pie.InnerRadius = 12;
                            pie.Fill = Brushes.Beige;
                            pie.Opacity = 0.8;
                            Canvas.SetLeft(pie, f.Position.X + 6);
                            Canvas.SetTop(pie, f.Position.Y + 6);
                            PopupStage.Children.Add(pie);
                            pie.BeginAnimation(PiePiece.WedgeAngleProperty, new DoubleAnimation(360, new Duration(TimeSpan.FromMilliseconds(holdTime))));
                            startTimer.Tag = pie;
                            startTimer.Start();
                        }
                        break;
                    case FingerEventType.FINGER_UP_EVENT:
                    case FingerEventType.FINGER_OUT_EVENT:
                        if (_holdFingersList.ContainsKey(f.FingerID))
                        {
                            DispatcherTimer timer = _holdFingersList[f.FingerID];
                            timer.Stop();
                            PiePiece pie = timer.Tag as PiePiece;
                            pie.BeginAnimation(PiePiece.WedgeAngleProperty, null);
                            PopupStage.Children.Remove(pie);

                            _holdFingersList.Remove(f.FingerID);
                            _holdFingerPositionList.Remove(f.FingerID);
                        }
                        break;
                    case FingerEventType.FINGER_MOVE_EVENT:
                        if (_holdFingersList.ContainsKey(f.FingerID))
                        {
                            double distance = (f.Position - _holdFingerPositionList[f.FingerID]).Length;
                            if (distance > holdRadius)
                            {
                                DispatcherTimer timer = _holdFingersList[f.FingerID];
                                timer.Stop();
                                PiePiece pie = timer.Tag as PiePiece;
                                pie.BeginAnimation(PiePiece.WedgeAngleProperty, null);
                                PopupStage.Children.Remove(pie);

                                _holdFingersList.Remove(f.FingerID);
                                _holdFingerPositionList.Remove(f.FingerID);
                            }
                        }
                        break;
                }
            }
        }

        void startTimer_Elapsed(object sender, EventArgs e)
        {
            DispatcherTimer timer = sender as DispatcherTimer;
            timer.Stop();
            if (_holdFingersList.ContainsValue(timer))
            {
                // pop up the menu
                int finger = 0;
                foreach (int f in _holdFingersList.Keys)
                {
                    if (_holdFingersList[f] == timer)
                    {
                        finger = f;
                        break;
                    }
                }

                UElementMenu menu = new UElementMenu();
                UElementMenuItem shutItem = new UElementMenuItem();
                shutItem.Header = "Shut down";
                shutItem.Command = "ShutDown";
                UElementMenuItem loadItem = new UElementMenuItem();
                loadItem.Header = "Load application";
                loadItem.Command = "LoadApplication";
                UElementMenuItem resetItem = new UElementMenuItem();
                resetItem.Header = "Reset tabletop";
                resetItem.Command = "ResetTabletop";
                menu.Items.Add(shutItem);
                menu.Items.Add(loadItem);
                menu.Items.Add(resetItem);
                menu.MaxAngle = 320;
                Random r = new Random();
                menu.Orientation = r.NextDouble() * 360;

                OnMenuShowing(menu);

                topZindex++;
                Canvas.SetZIndex(menu, topZindex);
                Canvas.SetLeft(menu, _holdFingerPositionList[finger].X - 30);
                Canvas.SetTop(menu, _holdFingerPositionList[finger].Y - 30);
                menu.Opacity = 1;
                menu.Width = 60;
                menu.Height = 60;
                _menuPositionList.Add(menu, _holdFingerPositionList[finger]);
                _menuList.Add(menu, finger);
                _holdFingersList.Remove(finger);
                _holdFingerPositionList.Remove(finger);
                PopupStage.Children.Add(menu);

                PiePiece pie = timer.Tag as PiePiece;
                pie.BeginAnimation(PiePiece.WedgeAngleProperty, null);
                PopupStage.Children.Remove(pie);

                menu.Loaded += new RoutedEventHandler(menu_Loaded);
                menu.SubmenuClosed += new EventHandler(menu_SubmenuClosed);
                menu.CommandReceived += new ElementMenuCommandReceivedEventHandler(OnCommandReceived);
            }
        }

        protected virtual void OnMenuShowing(UElementMenu menu)
        {

        }

        protected virtual void OnCommandReceived(UElementMenu sender, ElementMenuCommandReceivedEventArgs args)
        {
            switch (args.Command)
            {
                case "ShutDown":
                    UTableHelper.Shutdown(0);
                    break;
                case "LoadApplication":
                    // Create the AppLoader object on the tabletop
                    ObjectCreateParameter loadAppParam = new ObjectCreateParameter(typeof(UAppLoader));
                    IObject appLoader = UTableHelper.CreateObject(loadAppParam);
                    PlaceAppLoader(appLoader, this._menuPositionList[sender]);
                    appLoader.Owner = this;
                    break;
                case "ResetTabletop":
                    ClearTabletop();
                    break;
            }
        }

        protected virtual void PlaceAppLoader(IObject appLoader, Point holdPosition)
        {
            Point expectedPosition = new Point(holdPosition.X - appLoader.Width / 2 + 50, holdPosition.Y - appLoader.Height / 2 + 50);

            if (expectedPosition.X < 0)
                expectedPosition.X = 0;
            else if (expectedPosition.X + appLoader.Width > this.Width)
                expectedPosition.X = this.Width - appLoader.Width;
            if (expectedPosition.Y < 0)
                expectedPosition.Y = 0;
            else if (expectedPosition.Y + appLoader.Height > this.Height)
                expectedPosition.Y = this.Height - appLoader.Height;
            appLoader.Position = expectedPosition;
            
            // TODO: change the orientation of the application according its position
        }

        protected virtual void OnMenuClosed(UElementMenu menu)
        {

        }

        void menu_Loaded(object sender, RoutedEventArgs e)
        {
            UElementMenu menu = sender as UElementMenu;
            if (!_menuList.ContainsKey(menu))
            {
                // remove the menu
                PopupStage.Children.Remove(menu);
                return;
            }

            int finger = _menuList[menu];
            // raise the finger down event on that menu so that it can be open when it is shown
            FingerEventArgs a = new FingerEventArgs(FingerEventType.FINGER_DOWN_EVENT, finger, new Point(20, 20));
            Collection<FingerEventArgs> c = new Collection<FingerEventArgs>();
            c.Add(a);
            menu.RaiseEvent(new MultiTouchEventArgs(c, 0));
        }

        void menu_SubmenuClosed(object sender, EventArgs e)
        {
            UElementMenu menu = sender as UElementMenu;

            if (!_menuList.ContainsKey(menu))
            {
                // remove the menu
                PopupStage.Children.Remove(menu);
                return;
            }
            OnMenuClosed(menu);

            PopupStage.Children.Remove(menu);
            _menuList.Remove(menu);
            _menuPositionList.Remove(menu);
        }

        #endregion

		#region Public Methods

        public void ClearTabletop()
        {
            foreach (IObject obj in this.OwnedObjects)
            {
                obj.Close();
            }
        }

		#endregion

		#region Override Methods

		public override void OnMessageReceived(object message)
        {
            base.OnMessageReceived(message);
        }

        #endregion

        #region Properties

        protected Dictionary<UElementMenu, Point> MenuPositionList { get { return _menuPositionList; } }

        #endregion
    }
}
